#import <Foundation/Foundation.h>
#import <Foundation/NSTimer.h>
#import "MTTetrominosEngine.h"

@implementation MTTetrominosEngine : NSObject

-(void) startEngine
{
	[_TetrominosBoard release];
	_TetrominosBoard = [[MTTetrominosBoard alloc] init];
	[_TetrominosBoard clearField];
	_xPos = SPAWN_X;
	_yPos = SPAWN_Y;
	_done = false;
	_score = 0;
	_level = 1;
	_gameSpeed = (48.0f/60.0f);
	
	// Seed random number generator.
	srand( time(NULL));
	
	_currPiece = nil;
	_nextPiece = nil;
}

-(void) saveStateWithFilename: (NSString*) path
{
	NSMutableArray* board = [NSMutableArray arrayWithCapacity: [_TetrominosBoard getWidth] * [_TetrominosBoard getHeight]];
	int x, y;
	for (y = 0; y < [_TetrominosBoard getHeight]; y++)
	{
		for (x = 0; x < [_TetrominosBoard getWidth]; x++)
		{
			[board addObject: [NSNumber numberWithInt: [_TetrominosBoard getPiece: x: y]]];
		}
	}
		
	NSDictionary* gameState = [NSDictionary dictionaryWithObjectsAndKeys: 
								board, @"MTBoard",
								[NSNumber numberWithBool: _done], @"MTGameOver",
								[NSNumber numberWithInt: _score], @"MTScore",
								[NSNumber numberWithInt: _level], @"MTLevel",
								[NSNumber numberWithInt: _xPos], @"MTXPos",
								[NSNumber numberWithInt: _yPos], @"MTYPos",
								[NSNumber numberWithInt: [_currPiece getOrientation]], @"MTCurrOrientation",
								[NSNumber numberWithInt: [_currPiece pieceType]], @"MTCurrPieceType",
								[NSNumber numberWithInt: [_nextPiece pieceType]], @"MTNextPieceType",
								nil];
	
	NSString* error;
	NSData* rawPList = [NSPropertyListSerialization dataFromPropertyList: gameState
		format: NSPropertyListXMLFormat_v1_0
		errorDescription: &error];
		
	[rawPList writeToFile: path atomically: YES];
}

-(void) loadStateWithFilename: (NSString*) path delete: (BOOL)delFlag
{
	NSLog(@"MobileTetrominos entered loadStateWithFilename");
	
	if(![[NSFileManager defaultManager] fileExistsAtPath: path])
	{		
		NSLog(@"MobileTetrominos exited loadStateWithFilename");
		return;
	}
	NSDictionary* gameState = [NSDictionary dictionaryWithContentsOfFile: path];
	NSArray* board = [gameState objectForKey: @"MTBoard"];
	
	int i;
	for (i = 0; i < [board count]; i++)
	{
		[_TetrominosBoard setPiece: 
			(i % [_TetrominosBoard getWidth]): 
			(i / [_TetrominosBoard getWidth]):
			[[board objectAtIndex: i] intValue]];
	}
	
	_score = [[gameState objectForKey: @"MTScore"] intValue];
	_level = [[gameState objectForKey: @"MTLevel"] intValue];
	_xPos = [[gameState objectForKey: @"MTXPos"] intValue];
	_yPos = [[gameState objectForKey: @"MTYPos"] intValue];
	
	_currPiece = [[MTTetrominosPiece alloc] initWithPiece: [[gameState objectForKey: @"MTCurrPieceType"] intValue]]; 
	[_currPiece setOrientation: [[gameState objectForKey: @"MTCurrOrientation"] intValue]];
	
	_nextPiece = [[MTTetrominosPiece alloc] initWithPiece: [[gameState objectForKey: @"MTNextPieceType"] intValue]];
	
	if(delFlag)
		[[NSFileManager defaultManager] removeFileAtPath: path handler:nil];
	
	NSLog(@"MobileTetrominos exited loadStateWithFilename");
}

-(void) reset
{
	[self startEngine];
}

-(int) level
{
	return _level;
}

-(float) gameSpeed
{
	return _gameSpeed;
}

-(int) score
{
	return _score;
}

-(MTTetrominosPiece*) nextPiece
{
	return _nextPiece;
}


-(int) getPiece: (int) x: (int) y
{
	int piece = [self currPiecePiece: x: y];
		
	if (piece == NOPIECE)
	{
		piece = [_TetrominosBoard getPiece: x: y];	
	}
	return piece;
}

-(int) currPiecePiece: (int)x : (int)y
{
	if((x >= (_xPos - [_currPiece originX])) && (x < (_xPos - [_currPiece originX]) + 4) && 
	   (y >= (_yPos - [_currPiece originY])) && (y < (_yPos - [_currPiece originY]) + 4))    // TODO: Remove magic number if desired.  4 = width of mask
	{
		int xPos = (x - (_xPos - [_currPiece originX]));
		int yPos = (y - (_yPos - [_currPiece originY]));
		return [_currPiece getMask: xPos: yPos];
	}
	
	return NOPIECE;
}

-(void) freezePiece
{
	int x, y;
	for (x = 0; x < [_TetrominosBoard getWidth]; x++)
	{
		for (y = 0; y < [_TetrominosBoard getHeight]; y++)
		{
			int currPiece = [self currPiecePiece: x: y];
			if (currPiece != NOPIECE)
			{
				[_TetrominosBoard setPiece: x: y: currPiece];
			}
		}
	}
}

-(void) heartbeat
{
	[self takeTurn];
}
 
-(BOOL) checkMove: (int)xBias: (int)yBias
{
	int i, j;
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			int maskVal = [_currPiece getMask: i: j];
			if (maskVal != NOPIECE)
			{
				int boardX = (_xPos - [_currPiece originX]) + i + xBias;
				int boardY = (_yPos - [_currPiece originY]) + j + yBias;
				if ([_TetrominosBoard getPiece: boardX: boardY] != NOPIECE)
				{
					return false;
				}
			}
		}
	} 
	
	return true;
} 

-(void) moveLeft
{
	if([self checkMove: -1: 0])
		_xPos--;
}

-(void) moveRight
{
	if([self checkMove: 1: 0])
		_xPos++;
}

-(void) rotateLeft
{
	switch ([_currPiece pieceType])
	{
		case O_SHAPE: break; //Really does nothing.
		case I_SHAPE: 
		case S_SHAPE: 
		case Z_SHAPE:
		{
			if ([_currPiece orientation] == NORMAL)
				[self setCurrPieceOrientation: CLOCKWISE];
			else
				[self setCurrPieceOrientation: NORMAL];
			break;
		}	
		case J_SHAPE:
		case L_SHAPE: 
		case T_SHAPE: 
		{
			switch ([_currPiece orientation])
			{	
				case NORMAL: [self setCurrPieceOrientation: COUNTERCLOCKWISE]; break;
				case CLOCKWISE: [self setCurrPieceOrientation: NORMAL]; break;
				case OPPOSITE: [self setCurrPieceOrientation: CLOCKWISE]; break;
				case COUNTERCLOCKWISE: [self setCurrPieceOrientation: OPPOSITE]; break;
			}
			break;
		}
	}
}

-(void) setCurrPieceOrientation: (int) orientation
{
	int oldOrientation = [_currPiece orientation];
	[_currPiece setOrientation: orientation];
	if (![self checkMove: 0: 0])
	{
		[_currPiece setOrientation: oldOrientation];
	}
}

-(void) rotateRight
{
	switch ([_currPiece pieceType])
	{
		case O_SHAPE: break; //Really does nothing.
		case I_SHAPE: 
		case S_SHAPE: 
		case Z_SHAPE:
		{
			if ([_currPiece orientation] == NORMAL)
				[self setCurrPieceOrientation: CLOCKWISE];
			else
				[self setCurrPieceOrientation: NORMAL];
			break;
		}	
		case J_SHAPE:
		case L_SHAPE: 
		case T_SHAPE: 
		{
			switch ([_currPiece orientation])
			{	
				case NORMAL: [self setCurrPieceOrientation: CLOCKWISE]; break;
				case CLOCKWISE: [self setCurrPieceOrientation: OPPOSITE]; break;
				case OPPOSITE: [self setCurrPieceOrientation: COUNTERCLOCKWISE]; break;
				case COUNTERCLOCKWISE: [self setCurrPieceOrientation: NORMAL]; break;
			}
			break;
		}
	}
}

-(BOOL) done
{
	return _done;
}
 
-(void) takeTurn
{
	//Do we have a piece?  If not, make one.
	if(_currPiece == nil)
	{
		if (_nextPiece == nil)
		{
			_nextPiece = [[MTTetrominosPiece alloc] initWithPiece: ((rand() % 7))]; 
		}
		_currPiece = _nextPiece;
		_nextPiece = [[MTTetrominosPiece alloc] initWithPiece: ((rand() % 7))]; 
		
		//if([self checkMove: 0: 0])
		if ([_TetrominosBoard getPiece: SPAWN_X: SPAWN_Y] != NOPIECE)
			_done = TRUE;
	}
	
	if ([self checkMove: 0: 1])
		_yPos++;
	else
	{
		[self freezePiece];
		[self removeCompletedLines];
		//Start new piece.
		_yPos = SPAWN_Y;
		_xPos = SPAWN_X;
		[_currPiece release];
		_currPiece = nil;
	}
}

-(void) removeCompletedLines
{
	int x, y;
	BOOL lineComplete;
	int linesRemoved = 0;
	for (y = 0; y < [_TetrominosBoard getHeight]; y++)
	{
		lineComplete = TRUE;
		for (x = 0; (x < [_TetrominosBoard getWidth]) && (lineComplete == TRUE); x++)
		{
			if([_TetrominosBoard getPiece: x: y] == NOPIECE)
			{
				lineComplete = FALSE;
			}
		}
		if(lineComplete == TRUE)
		{
			[_TetrominosBoard removeLine: y];
			linesRemoved++;
		}
	}
	_score += linesRemoved;
	_level = (_score / 10) + 1;
	
	// Fix game speed.
	_gameSpeed = (48.0/60.0)-(_level * .02);
	NSLog([[NSNumber numberWithFloat: _gameSpeed] stringValue]);
}

-(MTTetrominosBoard*) getField
{
	return _TetrominosBoard;
}

@end	